#pragma once

#include <cstdint>
#include <string>
#include <vector>
#include <unordered_map>
#include <memory>
#include "resource.h"

namespace cyb
{

enum class XmlFlag
{
	Optional,		// dont touch value if attribute doesen't exist
	Required		// error if attribute doesen't exist
};

class XmlAttribute
{
public:
	XmlAttribute( const std::string& name, const std::string& value );
	XmlAttribute( const std::string& name, const int8_t value );
	XmlAttribute( const std::string& name, const uint8_t value );
	XmlAttribute( const std::string& name, const int32_t value );
	XmlAttribute( const std::string& name, const uint32_t value );
	XmlAttribute( const std::string& name, const float value );
	XmlAttribute( const std::string& name, const double value );
	~XmlAttribute() = default;

	const std::string& Name() const;
	const std::string& Value() const;

	int8_t ByteValue() const;
	uint8_t UnsignedByteValue() const;
	int32_t IntValue() const;
	uint32_t UnsignedIntValue() const;
	bool BoolValue() const;
	float FloatValue() const;
	double DoubleValue() const;

private:
	std::string m_name;
	std::string m_value;
};

class XmlElement;
using XmlChildList = std::list<std::shared_ptr<XmlElement>>;

class XmlElement
{
public:
	XmlElement() = default;
	virtual ~XmlElement() = default;

	virtual const std::string& Name() const;
	void SetName( const std::string& name );
	void SetRoot( const XmlElement* element );

	XmlAttribute* Attribute( const std::string& name, const XmlFlag mode = XmlFlag::Optional ) const;
	void QueryAttribute( const std::string& name, std::string* value, const XmlFlag mode = XmlFlag::Optional ) const;
	void QueryAttribute( const std::string& name, int8_t* value, const XmlFlag mode ) const;
	void QueryAttribute( const std::string& name, uint8_t* value, const XmlFlag mode ) const;
	void QueryAttribute( const std::string& name, int32_t* value, const XmlFlag mode ) const;
	void QueryAttribute( const std::string& name, uint32_t* value, const XmlFlag mode ) const;
	void QueryAttribute( const std::string& name, bool* value, const XmlFlag mode ) const;
	void QueryAttribute( const std::string& name, float* value, const XmlFlag mode ) const;
	void QueryAttribute( const std::string& name, double* value, const XmlFlag mode ) const;
	
	void AddAttribute( XmlAttribute& attrib );
	void AddChildElement( std::shared_ptr<XmlElement> element );
	XmlChildList& ChildList( const std::string& name );
	std::shared_ptr<XmlElement> FirstChildElement( const std::string& name ) const;
	bool CheckElementName( const std::string& name, XmlFlag flag ) const;
	void Reset();

private:
	std::string m_name;
	XmlChildList m_children;
	std::unordered_map<std::string, XmlChildList> m_childMap;
	std::unordered_map<std::string, std::unique_ptr<XmlAttribute>> m_attributes;

protected:
	const XmlElement* m_root;
};

class XmlResource : public XmlElement, public FileResource
{
public:
	XmlResource( const std::string& filename );
	virtual ~XmlResource() = default;

	virtual const std::string& RootElementName() const { return XmlElement::Name(); }
	virtual bool Load();
	virtual void UnLoad();
};

}	// namespace cyb